<?php
  /**
   *  @author  Opeykin A. <http://andrey.opeykin.ru> <developer@allframeworks.ru>
   *  @version 0.0.2
   *  @package filters
   *
   *  Ð¤Ð¸Ð»ÑŒÑ‚Ñ€ Ð¿Ñ€ÐµÐ´Ð½Ð°Ð·Ð½Ð°Ñ‡ÐµÐ½ Ð´Ð»Ñ� Ñ„Ð¸Ð»ÑŒÑ‚Ñ€Ð°Ñ†Ð¸Ð¸ Ð²Ñ…Ð¾Ð´Ð½Ñ‹Ñ… Ð´Ð°Ð½Ð½Ñ‹Ñ…, c Ñ†ÐµÐ»ÑŒÑŽ Ð¿Ñ€ÐµÐ´Ð¾Ñ‚Ð²Ñ€Ð°Ñ‚Ð¸Ñ‚ÑŒ xss Ð°Ñ‚Ð°ÐºÐ¸.
   *  Ð”Ð»Ñ� Ñ„Ð¸Ð»ÑŒÑ‚Ñ€Ð°Ñ†Ð¸Ð¸ Ð¸Ñ�Ð¿Ð¾Ð»ÑŒÐ·ÑƒÑŽÑ‚Ñ�Ñ� Ñ€ÐµÐ³ÑƒÐ»Ñ�Ñ€Ð½Ñ‹Ðµ Ð²Ñ‹Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ� Ð¸Ð· Ñ„Ñ€ÐµÐ¹Ð¼Ð²Ð¾Ñ€ÐºÐ° Kohana Ð²ÐµÑ€Ñ�Ð¸Ð¸ 2.3.4
   *
   *  @example
   *
   *  public function filters()
   *  {
   *         return array(
   *                 array(
   *                       'application.filters.XssFilter',
   *                       'clean'   => '*',
   *                       'tags'    => 'strict',
   *                       'actions' => 'all'
   *                 )
   *         );
   *
   *   }
   *
   *   ÐžÐ¿Ð¸Ñ�Ð°Ð½Ð¸Ðµ Ð¿Ð°Ñ€Ð°Ð¼ÐµÑ‚Ñ€Ð¾Ð²
   *
   *   Ð’ ÐºÐ°Ñ‡ÐµÑ‚Ð²Ðµ Ð¿Ð°Ñ€Ð°Ð¼ÐµÑ‚Ñ€Ð° 'clean' Ð¼Ð¾Ð³ÑƒÑ‚ Ð±Ñ‹Ñ‚ÑŒ:
   *  - 'all' - Ñ„Ð¸Ð»ÑŒÑ‚Ñ€ÑƒÑŽÑ‚Ñ�Ñ� GET,POST,COOKIE,FILES Ð¼Ð°Ñ�Ñ�Ð¸Ð²Ñ‹;
   *  - '*'   - Ð°Ð½Ð°Ð»Ð¾Ð³ ALL;
   *  - Ñ‚Ð°Ðº Ð¶Ðµ Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾ Ñ�Ð¾Ñ‡ÐµÑ‚Ð°Ð½Ð¸Ðµ Ð»ÑŽÐ±Ñ‹Ñ… Ð¸Ð· Ð¿Ð°Ñ€Ð°Ð¼ÐµÑ‚Ñ€Ð¾Ð², Ð½Ð°Ð¿Ñ€Ð¸Ð¼ÐµÑ€ GET,COOKIE Ð¸Ð»Ð¸ POST,FILES
   *   Ð’ ÐºÐ°Ñ‡ÐµÑ�Ñ‚Ð²Ðµ Ð¿Ð°Ñ€Ð°Ð¼ÐµÑ‚Ñ€Ð° 'tags' Ð¼Ð¾Ð³ÑƒÑ‚ Ð±Ñ‹Ñ‚ÑŒ:
   *  - 'strict' - ÐºÐ¾ Ð²Ñ�ÐµÐ¼ Ð¿Ð°Ñ€Ð°Ð¼ÐµÑ‚Ñ€Ð°Ð¼ Ð¿Ñ€Ð¸Ð¼ÐµÐ½Ñ�ÐµÑ‚Ñ�Ñ� Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ� strip_tags (Ð¸Ñ�Ð¿Ð¾Ð»ÑŒÐ·ÑƒÐµÑ‚Ñ�Ñ� Ð¿Ð¾ ÑƒÐ¼Ð¾Ð»Ñ‡Ð°Ð½Ð¸ÑŽ)
   *  - 'soft'   - ÐºÐ¾ Ð²Ñ�ÐµÐ¼ Ð¿Ð°Ñ€Ð°Ð¼ÐµÑ‚Ñ€Ð°Ð¼ Ð¿Ñ€Ð¸Ð¼ÐµÐ½Ñ�ÐµÑ‚Ñ�Ñ� Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ� htmlspecialchars
   *  - 'none'   - Ð½Ðµ Ñ„Ð¸Ð»ÑŒÑ‚Ñ€Ð¾Ð²Ð°Ñ‚ÑŒ
   *   Ð’ ÐºÐ°Ñ‡ÐµÑ�Ñ‚Ð²Ðµ Ð¿Ð°Ñ€Ð°Ð¼ÐµÑ‚Ñ€Ð° 'actions' Ð¼Ð¾Ð³ÑƒÑ‚ Ð±Ñ‹Ñ‚ÑŒ:
   *  - '*' Ð¸Ð»Ð¸ 'all' - Ñ„Ð¸Ð»ÑŒÑ‚Ñ€ÑƒÑŽÑ‚Ñ�Ñ� Ð²Ñ�Ðµ Ñ�ÐºÑˆÐµÐ½Ñ‹
   *  - Ð¼Ð¾Ð¶Ð½Ð¾ ÑƒÐºÐ°Ð·Ð°Ñ‚ÑŒ Ñ�ÐºÑˆÐµÐ½Ñ‹ Ñ‡ÐµÑ€ÐµÐ· Ð·Ð°Ð¿Ñ�Ñ‚ÑƒÑŽ, Ð¿Ñ€Ð¸Ð¼ÐµÑ€
   *   'actions' => 'admin,manage' - Ñ„Ð¸Ð»ÑŒÑ‚Ñ€Ð¾Ð²Ð°Ñ‚ÑŒ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ Ñ�ÐºÑˆÐµÐ½Ñ‹ admin Ð¸ manage
   */

class YXssFilter extends CFilter
{

        public  $clean   = '*';
        public  $tags    = 'strict';
        public  $actions = '*,all';

        protected function preFilter($filterChain)
        {
        		$this->actions = trim(strtoupper($this->actions));
                // ÐµÑ�Ð»Ð¸ Ñ�ÐºÑˆÐ½ Ð¾Ð±Ñ€Ð°Ð±Ð°Ñ‚Ñ‹Ð²Ð°Ñ‚ÑŒ Ð½ÐµÑ‚ Ð½ÐµÐ¾Ð±Ñ…Ð¾Ð´Ð¸Ð¼Ð¾Ñ�Ñ‚Ð¸ - Ð¿Ñ€Ð¾Ñ�Ñ‚Ð¾ Ð²Ñ‹Ñ…Ð¾Ð´Ð¸Ð¼ Ð¸Ð· Ñ„Ð¸Ð»ÑŒÑ‚Ñ€Ð°
                if($this->actions != '*' && $this->actions != 'ALL' && !in_array($filterChain->action->id,explode(',',$this->actions)))
                {
                    return true;
                }
                $this->clean  = trim(strtoupper($this->clean));
                $this->tags   = trim(strtoupper($this->tags));
                $data = array(
                         'GET'    => &$_GET,
                         'POST'   => &$_POST,
                         'COOKIE' => &$_COOKIE,
                         'FILES'  => &$_FILES
                );

                if($this->clean === 'ALL' || $this->clean === '*')
                {
                        $this->clean = 'GET,POST,COOKIE,FILES';
                }

                // Ð¿Ð¾ ÑƒÐ¼Ð¾Ð»Ñ‡Ð°Ð½Ð¸ÑŽ - strict
                if(!in_array($this->tags,array('STRICT','SOFT','NONE')))
                {
                        $this->tags = 'STRICT';
                }



                $dataForClean = explode(',',$this->clean);
                if(count($dataForClean))
                {
                        foreach ($dataForClean as $key => $value)
                        {
                                if(isset ($data[$value]) && count($data[$value]))
                                {
                                        $this->doXssClean($data[$value]);
                                }
                        }
                }

              return true;
        }


        private function doXssClean(&$data)
        {
                if(is_array($data) && count($data))
                {
                       foreach($data as $k => $v)
                       {
                               $data[$k] = $this->doXssClean($v);
                       }
                       return $data;
                }

                if(trim($data) === '')
                {
                     return $data;
                }


                // Ð¿ÐµÑ€ÐµÐ´ Ñ„Ð¸Ð»ÑŒÑ‚Ñ€Ð°Ñ†Ð¸ÐµÐ¹ Ñ€Ð°Ð·Ð±ÐµÑ€ÐµÐ¼Ñ�Ñ� Ñ� Ñ‚ÐµÐ³Ð°Ð¼Ð¸
                switch ($this->tags)
                {
                        case 'STRICT':
                        	$data = strip_tags($data);
                        break;
                        case 'SOFT':
                        	$data = htmlentities($data,ENT_QUOTES,'UTF-8');
						break;
                        case 'NONE':
                        	$data = $data;
                        	//return null ;
                         break;
                        // Ð¿Ð¾ ÑƒÐ¼Ð¾Ð»Ñ‡Ð°Ð½Ð¸ÑŽ - strict
                        default:
                        	$data = strip_tags($data);
                        break;
                }

                // xss_clean function from Kohana framework 2.3.4
                $data = str_replace(array('&amp;','&lt;','&gt;'), array('&amp;amp;','&amp;lt;','&amp;gt;'), $data);
	        $data = preg_replace('/(&#*\w+)[\x00-\x20]+;/u', '$1;', $data);
	        $data = preg_replace('/(&#x*[0-9A-F]+);*/iu', '$1;', $data);
	        $data = html_entity_decode($data, ENT_COMPAT, 'UTF-8');
                // Remove any attribute starting with "on" or xmlns
                $data = preg_replace('#(<[^>]+?[\x00-\x20"\'])(?:on|xmlns)[^>]*+>#iu', '$1>', $data);
                // Remove javascript: and vbscript: protocols
                $data = preg_replace('#([a-z]*)[\x00-\x20]*=[\x00-\x20]*([`\'"]*)[\x00-\x20]*j[\x00-\x20]*a[\x00-\x20]*v[\x00-\x20]*a[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2nojavascript...', $data);
		$data = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*v[\x00-\x20]*b[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2novbscript...', $data);
		$data = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*-moz-binding[\x00-\x20]*:#u', '$1=$2nomozbinding...', $data);
                // Only works in IE: <span style="width: expression(alert('Ping!'));"></span>
                $data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?expression[\x00-\x20]*\([^>]*+>#i', '$1>', $data);
		$data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?behaviour[\x00-\x20]*\([^>]*+>#i', '$1>', $data);
		$data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:*[^>]*+>#iu', '$1>', $data);
                // Remove namespaced elements (we do not need them)
                $data = preg_replace('#</*\w+:\w[^>]*+>#i', '', $data);
                do
                {
                        // Remove really unwanted tags
                        $old_data = $data;
                        $data = preg_replace('#</*(?:applet|b(?:ase|gsound|link)|embed|frame(?:set)?|i(?:frame|layer)|l(?:ayer|ink)|meta|object|s(?:cript|tyle)|title|xml)[^>]*+>#i', '', $data);
                }
                while ($old_data !== $data);
                return $data;
        }

}
?>
